/*
    This file is part of qmessages.
    Copyright (C) 2010  Jason Newton <nevion@gmail.com>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as
    published by the Free Software Foundation, either version 3 of the
    License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
#include "QMessageEndpoint.h"
#include <QDebug>
#include <cassert>

namespace qmsgio{

QMessageEndpoint::QMessageEndpoint(QIODevice *transport, QObject *parent):QObject(parent), transport(transport), receive_state(MESSAGE_START){
    transport->setParent(this);
    connect(transport, SIGNAL(readyRead()), this, SLOT(onReceiveData()));
}

QMessageEndpoint::~QMessageEndpoint(){
}

#define DStream(device)              \
    QDataStream stream(device);              \
    stream.setVersion(QDataStream::Qt_4_6);\

#define buffer_and_transition(next_state)                               \
    qint32 nconsume = std::min((int) stream.device()->bytesAvailable(), \
            buffer_nexpect - buffer_nread);                              \
    if(buffer.size() < buffer_nread + nconsume){                        \
        buffer.resize(buffer_nread + nconsume);                         \
    }                                                                   \
    buffer_nread += stream.readRawData(                                 \
            const_cast<char *>(buffer.constData()) + buffer_nread,      \
            nconsume);                                                  \
    if(buffer_nread == buffer_nexpect){                                 \
        receive_state = next_state;                                     \
        goto loop;                                                      \
    }


void QMessageEndpoint::assemble(){
loop:
    switch(receive_state){
        case MESSAGE_START:{
            buffer_nread = 0;
            buffer_nexpect = QMessage::minMessageSize();
            buffer.resize(buffer_nexpect);
            receive_state = BUFFER_MINHEADER;
            goto loop;
            break;
        }case BUFFER_MINHEADER:{
            DStream(transport);
            buffer_and_transition(READ_MINHEADER);
            break;
        }case READ_MINHEADER:{
            DStream(buffer);
            msg.reset();
            msg = QMessage::parseMessageHeader(stream);
            receive_state = BUFFER_FIXED;
            buffer_nread = 0;
            buffer_nexpect = msg->fixedSize();
            buffer.resize(buffer_nexpect);
            goto loop;
            break;
        }case BUFFER_FIXED:{
            DStream(transport);
            buffer_and_transition(READ_FIXED);
            break;
        }case READ_FIXED:{
            DStream(buffer);
            if(msg->parseFixed(stream)){
                receive_state = BUFFER_VARIABLE;
                buffer_nread = 0;
                buffer_nexpect = msg->variableSize();
                buffer.resize(buffer_nexpect);
            }else{
                if(onInvalidMsg(msg.get(), receive_state)){
                    receive_state = MESSAGE_START;
                    goto loop;
                }
            }
            goto loop;
            break;
        }case BUFFER_VARIABLE:{
            DStream(transport);
            buffer_and_transition(READ_VARIABLE);
            break;
        }case READ_VARIABLE:{
            DStream(buffer);
            if(msg->parseVariable(stream)){
                receive_state = MESSAGE_COMPLETE;
                goto loop;
            }else{
                //failed to parse it and recovered, continue?
                if(onInvalidMsg(msg.get(), receive_state)){
                    receive_state = MESSAGE_START;
                    goto loop;
                }
            }
            break;
        }case MESSAGE_COMPLETE:{
            onReceiveMsg(msg.get());
            receive_state = MESSAGE_START;
            goto loop;
            break;
        }
    }
}

void QMessageEndpoint::onReceiveData(){
    if(transport->bytesAvailable() == 0){
        return;
    }
    assemble();
}

bool QMessageEndpoint::onReceiveMsg(QMessage *msg){
    emit receiveMsg(msg);
    return false;
}

bool QMessageEndpoint::onInvalidMsg(QMessage *msg, receive_state_t receive_state){
    qCritical()<<"Invalid message consumed!"<<typeid(*msg).name()<<receive_state;
    emit invalidMsg(msg);
    return false;
}

void QMessageEndpoint::sendMsg(QMessage *msg){
    DStream(transport);
    msg->serialize(stream);
}

}
